#ifndef     __MASTER_CONN_H
#define     __MASTER_CONN_H

#include "../conn/resp_future.h"
#include "../utils/object.h"
#include "../rtos/mutex.h"
#include "../utils/encode.h"
#include "../utils/task_info.h"

namespace service {

class MasterConn : private utils::NoCopyable {

public:
    static constexpr uint32_t FUTURE_COUNT = 3;
    static constexpr uint8_t SEQ_MAX = 15;

    template <typename... Ts>
	utils::Error simple_request(conn::Message &msg, uint8_t dest, uint8_t req, Ts&&... ts) {
        using namespace conn;
        using namespace utils;
        RespFuture *fut = nullptr;
        Error err;
        {
            AUTO_LOCK(m_mutex);
            ConnInfo conn_info(req, get_seq(), ConnInfo::TYPE_REQUEST, MY_ADDR, dest);
            
            fut = get_future(conn_info.request_id());
            if (fut == nullptr) {
                return ec::RESP_USE_UP;
            }

            uint8_t len = encode_all(m_write_buf, std::forward<Ts>(ts)...);
            err = write(conn_info, len);
        }

        if (err) {
            goto finish;
        }

        if (!fut->wait(msg, 1000)) {
            err = ec::SIMPLE_RESP_TIMEOUT;
            goto finish;
        }
    finish:
        fut->free();
        return err;
    }

    utils::Error query_task_info(utils::TaskInfo &task_info, uint8_t dest, uint8_t task_id = 0);

    template <typename... Ts>
    utils::Error async_request(conn::RespInfo &resp, uint8_t dest, uint8_t req, Ts&&... ts) {
        using namespace conn;
        using namespace utils;

        AUTO_LOCK(m_mutex);
        ConnInfo conn_info(req, get_seq(), ConnInfo::TYPE_REQUEST, MY_ADDR, dest);
        resp.ptr = get_future(conn_info.request_id());
        if (resp.ptr == nullptr) {
            return ec::RESP_USE_UP;
        }
        resp.dest = dest;
        resp.req = req;
        uint8_t len = encode_all(m_write_buf, std::forward<Ts>(ts)...);
        return write(conn_info, len);
    }

    utils::Error wait_for(const conn::RespInfo &fut, conn::Message &msg);

    void reset_all();

    void dispatch(const conn::Message &msg);

private:
    utils::Error write(conn::ConnInfo &conn_info, uint8_t len);

    uint8_t get_seq();
    conn::RespFuture *get_future(uint16_t req_id);

    rtos::Mutex m_mutex;
    uint8_t m_seq = 0;
    uint8_t m_write_buf[32];
    conn::RespFuture m_resp_futures[FUTURE_COUNT];
};

inline utils::Object<MasterConn> masterConn;

}




#endif
